package com.aust.annotation;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * 注解的本质（接口 + 动态代理）
 * 反射是连接“注解（元数据）”和“程序逻辑”的桥梁，是我们在运行时获取注解信息、触发动态代理返回注解值的手段。
 * 注解是接口形式的标签，注解值存储在 .class 文件的元数据中，JVM 在运行时使用反射 + 动态代理来生成注解实例，调用注解属性时由代理从元数据中返回值。
 */

/**
 * 注解是一个接口（@interface 本质上是 interface）；
 * JVM 读取 .class 文件中的注解元数据；
 * 通过动态代理自动生成这个接口的实现对象；
 * 调用注解属性方法时，代理对象会根据注解中的元数据返回值。
 */

/**
 * 动态代理底层原理：
 * 使用 Proxy.newProxyInstance() 方法动态生成一个代理类；
 * 生成的类会实现你指定的接口；
 * 你传入的 InvocationHandler 会拦截所有方法调用；
 * 方法调用时通过反射调用 invoke(proxy, method, args)。
 */
public class UserAnnotation {
    public static void main(String[] args) throws Exception {

        /**
         * 解析类上注解
         */
        Class<User> userClass = User.class;
        if (userClass.isAnnotationPresent(MyInfo.class)) {
            MyInfo myInfo = userClass.getAnnotation(MyInfo.class);
            System.out.println(myInfo.value());
        }

        /**
         * 解析字段上注解
         */
        Field field = userClass.getDeclaredField("name");
        field.setAccessible(true);
        if (field.isAnnotationPresent(MyInfo.class)) {
            MyInfo myInfo = field.getAnnotation(MyInfo.class);
            System.out.println(myInfo.value());
        }

        /**
         * 解析方法上注解
         */
        Method sayHello = userClass.getDeclaredMethod("sayHello");
        if (sayHello.isAnnotationPresent(MyInfo.class)) {
            MyInfo myInfo = sayHello.getAnnotation(MyInfo.class);
            System.out.println(myInfo.value());
        }

        /**
         * 解析构造器上注解
         */
        Constructor<User> constructor = userClass.getConstructor(String.class);
        if (constructor.isAnnotationPresent(MyInfo.class)) {
            MyInfo myInfo = constructor.getAnnotation(MyInfo.class);
            System.out.println(myInfo.value());
        }

        /**
         * 解析参数上注解
         */
        Parameter[] parameters = constructor.getParameters();
        for (Parameter parameter : parameters) {
            if (parameter.isAnnotationPresent(MyInfo.class)) {
                MyInfo myInfo = parameter.getAnnotation(MyInfo.class);
                System.out.println(myInfo.value());
            }
        }
    }
}
